"""
指数行情模块
获取指数行情数据
"""
import time
from typing import Dict, List, Optional
from datetime import datetime, timedelta
from loguru import logger

try:
    import okx.MarketData as MarketData
except ImportError:
    logger.warning("okx SDK未安装，请运行: pip install okx")
    MarketData = None


class IndexTickersManager:
    """指数行情管理器"""
    
    # 限速配置：20次/2s
    RATE_LIMIT_REQUESTS = 20
    RATE_LIMIT_WINDOW = 2.0  # 秒
    
    def __init__(self, sandbox: bool = True):
        """
        初始化指数行情管理器
        
        Args:
            sandbox: 是否使用模拟盘，True=模拟盘(1)，False=实盘(0)
        """
        if MarketData is None:
            raise ImportError("okx SDK未安装，请运行: pip install okx")
        
        self.sandbox = sandbox
        flag = "1" if sandbox else "0"
        
        # 初始化OKX行情数据API
        self.market_api = MarketData.MarketAPI(flag=flag)
        
        # 限速控制
        self.request_times: List[float] = []  # 请求时间戳列表
        
        # 数据缓存
        self.index_tickers_cache: Dict[str, Dict] = {}  # inst_id -> ticker_data
        self.cache_expire_time: Dict[str, datetime] = {}  # inst_id -> expire_time
        self.cache_duration = timedelta(seconds=10)  # 缓存10秒
        
        logger.info(f"指数行情管理器已初始化 (模式: {'模拟盘' if sandbox else '实盘'})")
    
    def _check_rate_limit(self):
        """
        检查并执行限速
        限速规则：20次/2s (IP)
        """
        now = time.time()
        
        # 移除2秒前的请求记录
        self.request_times = [t for t in self.request_times 
                             if now - t < self.RATE_LIMIT_WINDOW]
        
        # 如果达到限速上限，等待
        if len(self.request_times) >= self.RATE_LIMIT_REQUESTS:
            oldest_request = self.request_times[0]
            wait_time = self.RATE_LIMIT_WINDOW - (now - oldest_request)
            
            if wait_time > 0:
                logger.debug(f"触发限速，等待 {wait_time:.2f} 秒")
                time.sleep(wait_time)
                # 重新计算
                now = time.time()
                self.request_times = [t for t in self.request_times 
                                     if now - t < self.RATE_LIMIT_WINDOW]
        
        # 记录本次请求
        self.request_times.append(now)
    
    def get_index_tickers(self, inst_id: Optional[str] = None,
                         quote_ccy: Optional[str] = None,
                         use_cache: bool = True) -> List[Dict]:
        """
        获取指数行情
        
        Args:
            inst_id: 指数，如 BTC-USD
            quote_ccy: 指数计价单位，目前只有USD/USDT/BTC/USDC为计价单位的指数
            use_cache: 是否使用缓存
            
        Returns:
            指数行情列表，每个指数包含以下字段：
            - instId: 指数
            - idxPx: 最新指数价格
            - high24h: 24小时最高价格
            - low24h: 24小时最低价格
            - open24h: 24小时开盘价格
            - sodUtc0: UTC 0时开盘价
            - sodUtc8: UTC+8时开盘价
            - ts: 指数价格更新时间（毫秒时间戳）
        """
        # 生成缓存键
        cache_key = f"{inst_id}_{quote_ccy}"
        
        # 检查缓存
        if use_cache and inst_id and cache_key in self.index_tickers_cache:
            expire_time = self.cache_expire_time.get(cache_key)
            if expire_time and datetime.now() < expire_time:
                logger.debug(f"从缓存获取指数行情: {cache_key}")
                return [self.index_tickers_cache[cache_key]]
        
        # 执行限速检查
        self._check_rate_limit()
        
        try:
            # 构建请求参数
            params = {}
            if inst_id:
                params["instId"] = inst_id
            if quote_ccy:
                params["quoteCcy"] = quote_ccy
            
            logger.info(f"获取指数行情: {params if params else '所有指数'}")
            result = self.market_api.get_index_tickers(**params)
            
            # 检查响应
            if result.get("code") != "0":
                error_msg = result.get("msg", "未知错误")
                logger.error(f"获取指数行情失败: {error_msg}")
                raise RuntimeError(f"API错误: {error_msg}")
            
            # 提取数据
            data_list = result.get("data", [])
            logger.info(f"获取指数行情成功: {len(data_list)} 个指数")
            
            # 更新缓存
            if use_cache and inst_id and data_list:
                self.index_tickers_cache[cache_key] = data_list[0]
                self.cache_expire_time[cache_key] = datetime.now() + self.cache_duration
            
            return data_list
            
        except Exception as e:
            logger.error(f"获取指数行情异常: {e}")
            raise
    
    def get_index_ticker(self, inst_id: str, use_cache: bool = True) -> Optional[Dict]:
        """
        获取单个指数的行情
        
        Args:
            inst_id: 指数，如 BTC-USD
            use_cache: 是否使用缓存
            
        Returns:
            指数行情字典，如果不存在返回None
        """
        data_list = self.get_index_tickers(inst_id, use_cache=use_cache)
        
        if data_list:
            return data_list[0]
        return None
    
    def get_index_ticker_details(self, inst_id: str) -> Dict:
        """
        获取指数行情详细信息（包含格式化数据）
        
        Args:
            inst_id: 指数
            
        Returns:
            详细信息字典
        """
        ticker = self.get_index_ticker(inst_id)
        
        if not ticker:
            raise ValueError(f"无法获取指数行情: {inst_id}")
        
        # 提取数据
        idx_px = float(ticker.get("idxPx", 0))
        high_24h = float(ticker.get("high24h", 0))
        low_24h = float(ticker.get("low24h", 0))
        open_24h = float(ticker.get("open24h", 0))
        sod_utc0 = float(ticker.get("sodUtc0", 0))
        sod_utc8 = float(ticker.get("sodUtc8", 0))
        ts = int(ticker.get("ts", 0))
        
        # 计算涨跌幅
        change_24h = idx_px - open_24h
        change_24h_pct = (change_24h / open_24h * 100) if open_24h > 0 else 0
        
        change_utc0 = idx_px - sod_utc0
        change_utc0_pct = (change_utc0 / sod_utc0 * 100) if sod_utc0 > 0 else 0
        
        change_utc8 = idx_px - sod_utc8
        change_utc8_pct = (change_utc8 / sod_utc8 * 100) if sod_utc8 > 0 else 0
        
        # 格式化时间
        timestamp_str = datetime.fromtimestamp(ts / 1000).strftime("%Y-%m-%d %H:%M:%S")
        
        return {
            "inst_id": inst_id,
            "index_price": idx_px,
            "high_24h": high_24h,
            "low_24h": low_24h,
            "open_24h": open_24h,
            "change_24h": change_24h,
            "change_24h_pct": change_24h_pct,
            "sod_utc0": sod_utc0,
            "change_utc0": change_utc0,
            "change_utc0_pct": change_utc0_pct,
            "sod_utc8": sod_utc8,
            "change_utc8": change_utc8,
            "change_utc8_pct": change_utc8_pct,
            "timestamp": timestamp_str,
            "timestamp_ms": ts,
        }
    
    def get_multiple_index_tickers(self, inst_ids: List[str]) -> Dict[str, Dict]:
        """
        批量获取多个指数的行情
        
        Args:
            inst_ids: 指数列表
            
        Returns:
            字典，key为指数，value为行情信息
        """
        results = {}
        
        for inst_id in inst_ids:
            try:
                ticker = self.get_index_ticker(inst_id)
                results[inst_id] = ticker
            except Exception as e:
                logger.error(f"获取 {inst_id} 指数行情失败: {e}")
                results[inst_id] = None
        
        return results
    
    def compare_index_prices(self, inst_ids: List[str]) -> List[Dict]:
        """
        比较多个指数的价格
        
        Args:
            inst_ids: 指数列表
            
        Returns:
            比较结果列表（按价格从高到低排序）
        """
        results = []
        
        for inst_id in inst_ids:
            try:
                details = self.get_index_ticker_details(inst_id)
                results.append({
                    "inst_id": inst_id,
                    "index_price": details["index_price"],
                    "change_24h_pct": details["change_24h_pct"],
                })
            except Exception as e:
                logger.error(f"获取 {inst_id} 指数行情失败: {e}")
        
        # 按价格排序
        results.sort(key=lambda x: x["index_price"], reverse=True)
        
        return results
    
    def get_top_gainers(self, quote_ccy: str = "USD", limit: int = 10) -> List[Dict]:
        """
        获取涨幅最大的指数
        
        Args:
            quote_ccy: 计价货币
            limit: 返回数量
            
        Returns:
            涨幅最大的指数列表
        """
        tickers = self.get_index_tickers(quote_ccy=quote_ccy)
        
        # 计算涨跌幅
        ticker_with_change = []
        for ticker in tickers:
            idx_px = float(ticker.get("idxPx", 0))
            open_24h = float(ticker.get("open24h", 0))
            
            if open_24h > 0:
                change_pct = (idx_px - open_24h) / open_24h * 100
                ticker_with_change.append({
                    "inst_id": ticker.get("instId"),
                    "index_price": idx_px,
                    "change_24h_pct": change_pct,
                })
        
        # 按涨幅排序
        ticker_with_change.sort(key=lambda x: x["change_24h_pct"], reverse=True)
        
        return ticker_with_change[:limit]
    
    def get_top_losers(self, quote_ccy: str = "USD", limit: int = 10) -> List[Dict]:
        """
        获取跌幅最大的指数
        
        Args:
            quote_ccy: 计价货币
            limit: 返回数量
            
        Returns:
            跌幅最大的指数列表
        """
        tickers = self.get_index_tickers(quote_ccy=quote_ccy)
        
        # 计算涨跌幅
        ticker_with_change = []
        for ticker in tickers:
            idx_px = float(ticker.get("idxPx", 0))
            open_24h = float(ticker.get("open24h", 0))
            
            if open_24h > 0:
                change_pct = (idx_px - open_24h) / open_24h * 100
                ticker_with_change.append({
                    "inst_id": ticker.get("instId"),
                    "index_price": idx_px,
                    "change_24h_pct": change_pct,
                })
        
        # 按跌幅排序
        ticker_with_change.sort(key=lambda x: x["change_24h_pct"])
        
        return ticker_with_change[:limit]
    
    def clear_cache(self, inst_id: Optional[str] = None):
        """
        清除缓存
        
        Args:
            inst_id: 指数，如果为None则清除所有缓存
        """
        if inst_id:
            # 清除所有包含该inst_id的缓存
            keys_to_remove = [key for key in self.index_tickers_cache.keys() 
                            if key.startswith(f"{inst_id}_")]
            for key in keys_to_remove:
                del self.index_tickers_cache[key]
                if key in self.cache_expire_time:
                    del self.cache_expire_time[key]
            logger.info(f"已清除 {inst_id} 的缓存")
        else:
            self.index_tickers_cache.clear()
            self.cache_expire_time.clear()
            logger.info("已清除所有指数行情缓存")
    
    def get_cache_stats(self) -> Dict:
        """
        获取缓存统计信息
        
        Returns:
            缓存统计字典
        """
        total_cached = len(self.index_tickers_cache)
        valid_cached = sum(1 for key in self.index_tickers_cache.keys()
                          if key in self.cache_expire_time and
                          datetime.now() < self.cache_expire_time[key])
        
        return {
            "total_cached": total_cached,
            "valid_cached": valid_cached,
            "expired_cached": total_cached - valid_cached,
            "cache_duration_seconds": self.cache_duration.total_seconds(),
        }


# 便捷函数
def get_index_tickers(inst_id: Optional[str] = None, sandbox: bool = True, **kwargs) -> List[Dict]:
    """
    便捷函数：获取指数行情
    
    Args:
        inst_id: 指数
        sandbox: 是否使用模拟盘
        **kwargs: 其他参数传递给get_index_tickers
        
    Returns:
        指数行情列表
    """
    manager = IndexTickersManager(sandbox=sandbox)
    return manager.get_index_tickers(inst_id, **kwargs)


def get_index_ticker_details(inst_id: str, sandbox: bool = True) -> Dict:
    """
    便捷函数：获取指数行情详细信息
    
    Args:
        inst_id: 指数
        sandbox: 是否使用模拟盘
        
    Returns:
        详细信息字典
    """
    manager = IndexTickersManager(sandbox=sandbox)
    return manager.get_index_ticker_details(inst_id)


def compare_index_prices(inst_ids: List[str], sandbox: bool = True) -> List[Dict]:
    """
    便捷函数：比较多个指数的价格
    
    Args:
        inst_ids: 指数列表
        sandbox: 是否使用模拟盘
        
    Returns:
        比较结果列表
    """
    manager = IndexTickersManager(sandbox=sandbox)
    return manager.compare_index_prices(inst_ids)


def get_top_gainers(quote_ccy: str = "USD", limit: int = 10, sandbox: bool = True) -> List[Dict]:
    """
    便捷函数：获取涨幅最大的指数
    
    Args:
        quote_ccy: 计价货币
        limit: 返回数量
        sandbox: 是否使用模拟盘
        
    Returns:
        涨幅最大的指数列表
    """
    manager = IndexTickersManager(sandbox=sandbox)
    return manager.get_top_gainers(quote_ccy, limit)


def get_top_losers(quote_ccy: str = "USD", limit: int = 10, sandbox: bool = True) -> List[Dict]:
    """
    便捷函数：获取跌幅最大的指数
    
    Args:
        quote_ccy: 计价货币
        limit: 返回数量
        sandbox: 是否使用模拟盘
        
    Returns:
        跌幅最大的指数列表
    """
    manager = IndexTickersManager(sandbox=sandbox)
    return manager.get_top_losers(quote_ccy, limit)
